package com.allen.app.common.utils;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.experimental.UtilityClass;

/**
 * @author Leon (234239150@qq.com) created in 16:05 18.9.15
 */
@UtilityClass
public class Maps {

    public static <K, V> Map<K, V> empty() {
        return Collections.emptyMap();
    }

    public static <K, V> MapProxy<K, V> proxy() {
        return new MapProxy<>();
    }

    public static <K, V> MapProxy<K, V> proxy(int initialCapacity) {
        return new MapProxy<>(initialCapacity);
    }

    public static <K, V> MapProxy<K, V> proxy(int initialCapacity, float loadFactor) {
        return new MapProxy<>(initialCapacity, loadFactor);
    }

    public static <K, V> MapProxy<K, V> proxy(Map<K, V> m) {
        return new MapProxy<>(m);
    }

    public static <K, V> MapBuilder<K, V> builder() {
        return new MapBuilder<>();
    }

    public static <K, V> MapBuilder<K, V> as(Map<K, V> source) {
        return new MapBuilder<>(source);
    }


    public static <K, V> Map<K, V> transform(Object m) {
        Args.assignable(Map.class, m.getClass());
        return transform((Map) m);
    }

    @SuppressWarnings("unchecked")
    public static <K, V> Map<K, V> transform(Map<?, ?> m) {
        return (Map<K, V>) m;
    }

    /**
     * @deprecated see {@link #transform(Object)}
     */
    public static <K, V> Map<K, V> generic(Object m) {
        return transform(m);
    }

    /**
     * @deprecated see {@link #transform(Map)}
     */
    public static <K, V> Map<K, V> generic(Map<?, ?> m) {
        return transform(m);
    }

    public static <K, V> Map<K, V> of(K key, V value) {
        return pairs(Pair.of(key, value));
    }

    public static <K, V> Map<K, V> of(K key1, V value1, K key2, V value2) {
        return pairs(Pair.of(key1, value1), Pair.of(key2, value2));
    }

    public static <K, V> Map<K, V> of(K key1, V value1, K key2, V value2, K key3, V value3) {
        return pairs(Pair.of(key1, value1), Pair.of(key2, value2), Pair.of(key3, value3));
    }

    public static <K, V> Map<K, V> of(K key1, V value1, K key2, V value2, K key3, V value3, K key4, V value4) {
        return pairs(Pair.of(key1, value1), Pair.of(key2, value2), Pair.of(key3, value3), Pair.of(key4, value4));
    }

    public static <K, V> Map<K, V> of(K key1, V value1, K key2, V value2, K key3, V value3, K key4, V value4, K key5, V value5) {
        return pairs(Pair.of(key1, value1), Pair.of(key2, value2), Pair.of(key3, value3), Pair.of(key4, value4), Pair
            .of(key5, value5));
    }

    @SafeVarargs
    public static <K, V> Map<K, V> pairs(Pair<K, V>... pairs) {
        return pairs(Arrays.asList(pairs));
    }

    public static <K, V> Map<K, V> pairs(List<Pair<K, V>> pairs) {
        Map<K, V> map = new LinkedHashMap<>(pairs.size());
        pairs.stream().filter(pair -> Objects.nonNull(pair.getFirst())).forEach(pair -> map.put(pair.getFirst(), pair.getSecond()));
        return map;
    }

    public static class MapBuilder<K, V> {
        private Map<K, V> map;

        MapBuilder() {
            this(new HashMap<>());
        }

        MapBuilder(Map<K, V> map) {
            this.map = map;
        }

        public MapBuilder<K, V> put(K key, V value) {
            this.map.put(key, value);
            return this;
        }

        public Map<K, V> build() {
            return this.map;
        }

        public <T> T cover(T source) {
            return Beans.from(this.map).to(source);
        }
    }
}
